Nihilium
The model Forgot password Under the hood Condition editor Scenarios
Developer toolkit

SDK

Nihilium is something you integrate, not a service you sign up for. The SDK splits a secret across processors and recovers it in a few lines of code.

It ships two high-level clients, one for sealing and one for unsealing. The cryptography, processor selection, and enforcement wiring sit behind them.

$ npm install @nihilium/client-sdk
The model

Two verbs: seal, then unseal.

Everything else is configuration. You seal a secret across n processors and require any k of them to bring it back.

Seal
Split the secret across k-of-n
Sealing produces a NihiliumSeal: one package per processor plus a single threshold seal. It is plain JSON, so you store it yourself. Sealing is the only paid step.
Unseal
Recover from any k of the n
Hand the stored seal back and name any k processors. The client publishes the reveal values, gathers the proofs, and returns the secret. Unsealing is never paid.
Resumable
A crash never re-charges
Both clients persist progress step by step. Re-run after a failure and they pick up where they left off, without paying processors twice or re-running proofs.
Start here ยท ZKEmail

"I forgot my password."

The most familiar recovery gesture there is. With the ZKEmail scenario, the user replies to a recovery email and a zero-knowledge proof of that reply opens the seal. No custodian holds the secret, and only the domain of the address is ever sent, never the address itself.

Try the live demo
forgot-password.ts
import {
ZKEmailSealingClient, ZKEmailUnsealingClient,
} from "@nihilium/client-sdk/scenarios/zkemail";
 
import { NihiliumPaymentProvider } from "@nihilium/client-sdk";
 
// Payment is charged once, upfront at sealing. Recovery is always free, without intermediaries.
const sealing = await ZKEmailSealingClient.create({
email: "user@example.com", threshold: 3, processorCount: 5,
payment: new NihiliumPaymentProvider("https://your-server.example"),
});
const seal = await sealing.seal("my-super-secret-password");
// Store it on the user's cloud, or keep it as the service provider:
// the seal is only useful to whoever controls the email address.
 
// Later, the user clicks "Forgot my password".
const unsealing = await ZKEmailUnsealingClient.fromSeal(seal);
 
// They reply to the recovery email; the proof does the rest.
const recovered = await unsealing.unseal(); // "my-super-secret-password"

The one phase that waits on a person is the email reply, so drive your UI off it: show "check your inbox" while it is open, and everything else resolves on its own.

Under the hood

The same two verbs, without a scenario.

Strip the recovery email away and this is what a scenario wraps: seal a value across three processors so any two can recover it, using the default reveal-only policy.

recover.ts
import {
NihiliumClient, createRevealOnlyCollection, NETWORK_IDS,
} from "@nihilium/client-sdk";
 
// Point the client at the registry network.
const client = new NihiliumClient({ network: NETWORK_IDS.ARBITRUM });
 
// A reveal-only policy: it opens the moment the reveal value is published.
const { collection, template } = createRevealOnlyCollection(NETWORK_IDS.ARBITRUM);
 
// Seal across 3 processors, any 2 required to recover (2-of-3).
const sealing = await client.sealingClient({ template, threshold: 2, processorCount: 3 });
const seal = await sealing.start_sealing(secret);
 
// Store the seal anywhere: it is plain JSON.
localStorage.setItem("vault", JSON.stringify(seal));
 
// Later: recover the secret from any 2 of the 3 processors.
const unsealing = await client.unsealingClient(seal, { collection });
const recovered = await unsealing.start_unsealing([0, 1]);

Sealing runs one zero-knowledge proof per processor and calls a paid endpoint, so supply a payment provider for authorized processors in production. Recovery adds no cost.

Condition editor

Compose a scenario visually.

The scenarios above are pre-built policies. The editor is how you build your own: drag modules from the library, wire their outputs together, and the seal opens only when the whole chain is satisfied. The graph below is the ZKEmail flow, built from three modules.

Nihilium condition editor
The Nihilium condition editor: a module library on the left, a canvas wiring an Opening Module into Hash Tie Verification into a ZK Email Module, and a Required User Inputs panel on the right.
A library of modules
Time windows, Merkle proofs, hash preimages, signature checks, ZKPassport, ZKEmail. Some modules can fork, so one seal can carry several independent recovery paths.
Wire outputs to inputs
Each module exposes typed sockets. Here the opening module feeds a hash-tie step, which feeds the ZK Email module, chaining the conditions into one proof.
Inputs surface automatically
The editor collects every value a recovery must supply, like the email address hash, into a required-inputs panel. Export the graph and the SDK drives it.
Scenarios

Ready-made policies for real recovery flows.

A scenario bundles an unseal policy with the client that drives it, so a caller supplies only the inputs that are genuinely theirs. Each lives under its own namespace and the set can grow without the core surface growing with it.

Reveal-only
Open on a published value
The default policy. The seal opens as soon as its reveal value is posted to the datastream. This is the one used in the example above.
ZKEmail
Recover with a reply email
Seal a value behind a recovery address. To unseal, the owner replies to a mail from the service and a zero-knowledge proof of that reply satisfies the conditions. Only the domain is ever sent, never the full address.
Add later
A one-way deposit vault
Sealing publishes a vault public key, so anyone holding the seal can encrypt more data into the same vault later, with no unseal and no network. Only a real k-of-n recovery can read it back.
Good to know

The seal is the only thing you have to keep.

There is no account, no key file to guard, and no server state on your side. Store the JSON seal wherever you like; it carries everything a recovery needs, including which processors and datastreams to talk to.

Seal once and pay once. Recover for free, from any k of your processors, with nothing to remember but the seal itself.
Overview Conditions Sealing Unsealing Threshold Properties Primitives SDK